home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 August: Tool Chest / Dev.CD Aug 00 TC Disk 2.toast / pc / sample code / quicktime / quicktime vr / vrscript.win / feature files / vreffects.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-06-23  |  17.2 KB  |  540 lines

  1. //////////
  2. //
  3. //    File:        VREffects.c
  4. //
  5. //    Contains:    QuickTime video effects support for QuickTime VR movies.
  6. //
  7. //    Written by:    Tim Monroe
  8. //                Parts modeled on ShowEffect sample code by Dan Crow.
  9. //
  10. //    Copyright:    © 1997-1998 by Apple Computer, Inc., all rights reserved.
  11. //
  12. //    Change History (most recent first):
  13. //
  14. //       <7>         04/24/98    rtm        added VRMoov_DoIdle call to VREffects_RunTransitionEffect, to service
  15. //                                    scene-wide sounds while a transition is occurring 
  16. //       <6>         02/26/98    rtm        reworked VREffects_SetupTransitionEffect to use QTVRUpdate, not CopyBits;
  17. //                                    now we work much better on Windows (finally!);
  18. //                                    added VRScript_CheckForExpiredCommand call to VREffects_RunTransitionEffect
  19. //       <5>         02/10/98    rtm        fixed source rectangles in VREffects_SetupTransitionEffect
  20. //       <4>         02/05/98    rtm        cleaned up GWorld handling; remember, lock those pixmaps!
  21. //       <3>         11/01/97    rtm        added Endian macros to VREffects_MakeEffectDescription
  22. //       <2>         10/29/97    rtm        further work: got node transition effects working on Mac
  23. //       <1>         10/27/97    rtm        first file
  24. //       
  25. //    This file provides functions to incorporate QuickTime video effects into QuickTime VR movies.
  26. //    QuickTime video effects were introduced with QuickTime 3.0; they support many SMPTE transitions
  27. //    and other special effects. This file defines functions that use QT video effects in these ways:
  28. //
  29. //    * run effects while transitioning between nodes (let's call these "transition effects")
  30. //    * (this space for rent)
  31. //
  32. //////////
  33.  
  34. //    TO DO:
  35. //    + implement parameter handling
  36. //    + currently only one transition per node pair is supported; allow multiple transition effects
  37.  
  38. // header files
  39. #include "VREffects.h"
  40.  
  41.  
  42. //////////
  43. //
  44. // VREffects_InitWindowData
  45. // Initialize for QuickTime video effects.
  46. //
  47. //////////
  48.  
  49. void VREffects_InitWindowData (WindowObject theWindowObject)
  50. {
  51.     ApplicationDataHdl            myAppData;
  52.  
  53.     myAppData = (ApplicationDataHdl)GetAppDataFromWindowObject(theWindowObject);
  54.     if (myAppData != NULL) {
  55.         (**myAppData).fSourceGWorld = NULL;
  56.         (**myAppData).fTargetGWorld = NULL;
  57.         (**myAppData).fSourceGWDesc = NULL;
  58.         (**myAppData).fTargetGWDesc = NULL;
  59.         (**myAppData).fActiveTransition = NULL;
  60.     }
  61. }
  62.  
  63.  
  64. //////////
  65. //
  66. // VREffects_DumpWindowData
  67. // Dump the window-specific data for QuickTime video effects.
  68. //
  69. //////////
  70.  
  71. void VREffects_DumpWindowData (WindowObject theWindowObject)
  72. {
  73.     ApplicationDataHdl            myAppData;
  74.     
  75.     myAppData = (ApplicationDataHdl)GetAppDataFromWindowObject(theWindowObject);
  76.     if (myAppData != NULL) {
  77.  
  78.         // dispose of transition effects GWorlds used by this window object
  79.         if ((**myAppData).fSourceGWorld != NULL) {
  80.             DisposeGWorld((**myAppData).fSourceGWorld);
  81.             (**myAppData).fSourceGWorld = NULL;
  82.         }
  83.         
  84.         if ((**myAppData).fTargetGWorld != NULL) {
  85.             DisposeGWorld((**myAppData).fTargetGWorld);
  86.             (**myAppData).fTargetGWorld = NULL;
  87.         }
  88.         
  89.         // dispose of the image descriptions
  90.         if ((**myAppData).fSourceGWDesc != NULL) {
  91.             DisposeHandle((Handle)(**myAppData).fSourceGWDesc);
  92.             (**myAppData).fSourceGWDesc = NULL;
  93.         }
  94.         
  95.         if ((**myAppData).fTargetGWDesc != NULL) {
  96.             DisposeHandle((Handle)(**myAppData).fTargetGWDesc);
  97.             (**myAppData).fTargetGWDesc = NULL;
  98.         }
  99.         
  100.     }
  101. }
  102.  
  103.  
  104. //////////
  105. //
  106. // VREffects_DoIdle
  107. // Do any effects-related processing that can or should occur at idle time.
  108. // Returns true if the caller should call QTVRUpdate, false otherwise.
  109. //
  110. //////////
  111.  
  112. Boolean VREffects_DoIdle (WindowObject theWindowObject)
  113. {
  114.     ApplicationDataHdl        myAppData;
  115.     Boolean                    myNeedUpdate = false;
  116.  
  117.     myAppData = (ApplicationDataHdl)GetAppDataFromWindowObject(theWindowObject);
  118.     if (myAppData != NULL) {
  119.     
  120.         // nothing needed for transition effects
  121.         
  122.     }
  123.     
  124.     return(myNeedUpdate);
  125. }
  126.         
  127.                 
  128. //////////
  129. //
  130. // VREffects_GetTransitionEffect
  131. // Return the first transition effect enlisted for the specified fromNode/toNode pair.
  132. //
  133. //////////
  134.  
  135. VRScriptTransitionPtr VREffects_GetTransitionEffect (WindowObject theWindowObject, UInt32 fromNodeID, UInt32 toNodeID)
  136. {
  137.     ApplicationDataHdl        myAppData = NULL;
  138.     VRScriptTransitionPtr    myPointer = NULL;
  139.     VRScriptTransitionPtr    myNext = NULL;
  140.     
  141.     myAppData = (ApplicationDataHdl)GetAppDataFromWindowObject(theWindowObject);
  142.     if (myAppData == NULL)
  143.         return(myPointer);
  144.         
  145.     // walk the transition effects list and see if any apply to this node
  146.     myPointer = (VRScriptTransitionPtr)(**myAppData).fListArray[kVREntry_TransitionEffect];
  147.     while (myPointer != NULL) {
  148.         myNext = myPointer->fNextEntry;
  149.         
  150.         if ((myPointer->fFromNodeID == fromNodeID) || (myPointer->fFromNodeID == kVRAnyNode))
  151.             if ((myPointer->fToNodeID == toNodeID) || (myPointer->fToNodeID == kVRAnyNode))
  152.                 return(myPointer);
  153.     
  154.         myPointer = myNext;
  155.     }
  156.     
  157.     return(myPointer);
  158. }
  159.  
  160.  
  161. //////////
  162. //
  163. // VREffects_MakeEffectDescription
  164. // Create an effect description.
  165. //
  166. // The effect description specifies which video effect is desired and the parameters for that effect.
  167. // It also describes the source(s) for the effect. An effect description is simply an atom container
  168. // that holds atoms with the appropriate information.
  169. //
  170. // Note that because we are creating an atom container, we must pass big-endian data (hence the calls
  171. // to EndianU32_NtoB).
  172. //
  173. // Currently we support up to two sources.
  174. //
  175. //////////
  176.  
  177. QTAtomContainer VREffects_MakeEffectDescription (long theEffectType, long theEffectNum, OSType theSourceName1, OSType theSourceName2)
  178. {
  179.     QTAtomContainer        mySample;
  180.     OSType                myType;
  181.     OSErr                myErr = noErr;
  182.  
  183.     // create a new, empty effect description
  184.     myErr = QTNewAtomContainer(&mySample);
  185.     if (myErr != noErr)
  186.         return(NULL);
  187.     
  188.     // create the effect ID atom: the atom type is kParameterWhatName, and the atom ID is kParameterWhatID
  189.     myType = EndianU32_NtoB(theEffectType);
  190.     myErr = QTInsertChild(mySample, kParentAtomIsContainer, kParameterWhatName, kParameterWhatID, 0, sizeof(myType), &myType, NULL);
  191.     if (myErr != noErr)
  192.         return(NULL);
  193.         
  194.     // add the first source
  195.     myType = EndianU32_NtoB(theSourceName1);
  196.     myErr = QTInsertChild(mySample, kParentAtomIsContainer, kEffectSourceName, 1, 0, sizeof(myType), &myType, NULL);
  197.                             
  198.     // add the second source
  199.     myType = EndianU32_NtoB(theSourceName2);
  200.     myErr = QTInsertChild(mySample, kParentAtomIsContainer, kEffectSourceName, 2, 0, sizeof(myType), &myType, NULL);
  201.     
  202.     // add a parameter for SMPTE effects
  203.     myType = EndianU32_NtoB(theEffectNum);
  204.     myErr = QTInsertChild(mySample, kParentAtomIsContainer, FOUR_CHAR_CODE('wpID'), 1, 0, sizeof(myType), &myType, NULL);
  205.     
  206.     return(mySample);
  207. }
  208.  
  209.  
  210. //////////
  211. //
  212. // VREffects_MakeSampleDescription
  213. // Create a description of an effect sample.
  214. //
  215. //////////
  216.  
  217. ImageDescriptionHandle VREffects_MakeSampleDescription (long theEffectType, short theWidth, short theHeight)
  218. {
  219.     ImageDescriptionHandle        mySampleDesc;
  220.  
  221.     // create a new sample description
  222.     mySampleDesc = (ImageDescriptionHandle)NewHandleClear(sizeof(ImageDescription));
  223.     if (mySampleDesc == NULL)
  224.         return(NULL);
  225.     
  226.     // fill in the fields of the sample description
  227.     (**mySampleDesc).idSize = sizeof(ImageDescription);
  228.     (**mySampleDesc).cType = theEffectType;
  229.     (**mySampleDesc).vendor = kAppleManufacturer;
  230.     (**mySampleDesc).temporalQuality = codecNormalQuality;
  231.     (**mySampleDesc).spatialQuality = codecNormalQuality;
  232.     (**mySampleDesc).width = theWidth;
  233.     (**mySampleDesc).height = theHeight;
  234.     (**mySampleDesc).hRes = 72L << 16;
  235.     (**mySampleDesc).vRes = 72L << 16;
  236.     (**mySampleDesc).dataSize = 0L;
  237.     (**mySampleDesc).frameCount = 1;
  238.     (**mySampleDesc).depth = 0;
  239.     (**mySampleDesc).clutID = -1;
  240.     
  241.     return(mySampleDesc);
  242. }
  243.  
  244.  
  245. //////////
  246. //
  247. // VREffects_SetupTransitionEffect
  248. // Prepare for a transition from the current node to the target node:
  249. // * make a copy of the current screen image
  250. // * set movie and controller GWorlds to an offscreen graphics world
  251. //
  252. //////////
  253.  
  254. OSErr VREffects_SetupTransitionEffect (WindowObject theWindowObject, UInt32 fromNodeID, UInt32 toNodeID)
  255. {
  256.     ApplicationDataHdl        myAppData = NULL;
  257.     VRScriptTransitionPtr    myPointer = NULL;
  258.     Rect                    myRect;
  259.     OSErr                    myErr = noErr;
  260.  
  261.     myAppData = (ApplicationDataHdl)GetAppDataFromWindowObject(theWindowObject);
  262.     if (myAppData == NULL)
  263.         return(paramErr);
  264.         
  265.     if (((**theWindowObject).fMovie == NULL) || ((**theWindowObject).fController == NULL))
  266.         return(paramErr);
  267.         
  268.     // lock the application data handle
  269.     HLock((Handle)myAppData);
  270.     
  271.     // see if any transition effects are enlisted for the current fromNode/toNode pair
  272.     myPointer = VREffects_GetTransitionEffect(theWindowObject, fromNodeID, toNodeID);
  273.     (**myAppData).fActiveTransition = myPointer;
  274.     if (myPointer == NULL)
  275.         goto bail;        // if there is no active transition effect, just bail
  276.  
  277.     // make sure our slate is clean
  278.     VREffects_DumpWindowData(theWindowObject);
  279.     VREffects_DumpEntryMem(myPointer);
  280.  
  281.     // get the size of the movie
  282.     GetMovieBox((**theWindowObject).fMovie, &myRect);
  283.     MacOffsetRect(&myRect, -myRect.left, -myRect.top);
  284.     
  285.     // create effect and sample descriptions            
  286.     myPointer->fEffectDesc = VREffects_MakeEffectDescription(myPointer->fEffectType, myPointer->fEffectNum, kSourceOneName, kSourceTwoName);
  287.     myPointer->fSampleDesc = VREffects_MakeSampleDescription(myPointer->fEffectType, myRect.right, myRect.bottom);
  288.     if ((myPointer->fEffectDesc == NULL) || (myPointer->fSampleDesc == NULL))
  289.         goto bail;
  290.  
  291.     // create an offscreen GWorld that is the size and depth of the movie window;
  292.     // this is the source of the transition effect
  293.     myErr = NewGWorld(&(**myAppData).fSourceGWorld, 0, &myRect, NULL, NULL, 0L);
  294.     if (myErr != noErr)
  295.         goto bail;
  296.  
  297.     LockPixels(GetGWorldPixMap((**myAppData).fSourceGWorld));    
  298.     
  299.     // create an offscreen GWorld that is the size and depth of the movie window;
  300.     // this is the destination of the transition effect
  301.     myErr = NewGWorld(&(**myAppData).fTargetGWorld, 0, &myRect, NULL, NULL, 0L);
  302.     if (myErr != noErr)
  303.         goto bail;
  304.  
  305.     LockPixels(GetGWorldPixMap((**myAppData).fTargetGWorld));
  306.     
  307.     // draw the current screen image into the source offscreen GWorld
  308.     SetMovieGWorld((**theWindowObject).fMovie, (CGrafPtr)(**myAppData).fSourceGWorld, NULL);
  309.     QTVRUpdate((**theWindowObject).fInstance, kQTVRCurrentMode);
  310.         
  311.     // set the movie and controller GWorlds to the destination GWorld
  312.     MCSetControllerPort((**theWindowObject).fController, (CGrafPtr)(**myAppData).fTargetGWorld);
  313.     SetMovieGWorld((**theWindowObject).fMovie, (CGrafPtr)(**myAppData).fTargetGWorld, NULL);
  314.     MCMovieChanged((**theWindowObject).fController, (**theWindowObject).fMovie);
  315.     
  316. bail:
  317.     // make sure we've got no transition scheduled if an error occurred
  318.     if (myErr != noErr)
  319.         (**myAppData).fActiveTransition = NULL;
  320.  
  321.     // unlock the application data handle
  322.     HUnlock((Handle)myAppData);
  323.     
  324.     return(myErr);
  325. }
  326.  
  327.  
  328. //////////
  329. //
  330. // VREffects_RunTransitionEffect
  331. // Run a transition from the previous node to the current node.
  332. //
  333. //////////
  334.  
  335. OSErr VREffects_RunTransitionEffect (WindowObject theWindowObject)
  336. {
  337.     ApplicationDataHdl            myAppData = NULL;
  338.     VRScriptTransitionPtr        myPointer = NULL;
  339.     ImageSequenceDataSource        mySrc1 = 0;
  340.     ImageSequenceDataSource        mySrc2 = 0;
  341.     ICMFrameTimeRecord            myFrameTime;
  342.     TimeValue                    myFrame;
  343.     PixMapHandle                mySrcPixMap;
  344.     PixMapHandle                myDstPixMap;
  345.     OSErr                        myErr = paramErr;
  346.  
  347.     myAppData = (ApplicationDataHdl)GetAppDataFromWindowObject(theWindowObject);
  348.     if (myAppData == NULL)
  349.         goto bail;
  350.     
  351.     // if there is no active transition effect, just bail
  352.     myPointer = (**myAppData).fActiveTransition;
  353.     if (myPointer == NULL)
  354.         goto bail;
  355.     
  356.     if ((myPointer->fEffectDesc == NULL) || (myPointer->fSampleDesc == NULL))
  357.         goto bail;
  358.         
  359.     // make an effects sequence
  360.     HLock((Handle)myPointer->fEffectDesc);
  361.     
  362.     // prepare the decompression sequence for playback    
  363.     myErr = DecompressSequenceBeginS(
  364.                             &(myPointer->fSequenceID),
  365.                             myPointer->fSampleDesc,
  366.                             StripAddress(*((Handle)myPointer->fEffectDesc)),
  367.                             GetHandleSize((Handle)myPointer->fEffectDesc),
  368.                             (CGrafPtr)GetPortFromWindowReference((**theWindowObject).fWindow),
  369.                             NULL,
  370.                             NULL,
  371.                             NULL,
  372.                             ditherCopy,
  373.                             NULL,
  374.                             0,
  375.                             codecNormalQuality,
  376.                             NULL);
  377.  
  378.     HUnlock((Handle)myPointer->fEffectDesc);
  379.     if (myErr != noErr)
  380.         goto bail;
  381.  
  382.     // get the pixel maps for the GWorlds
  383.     mySrcPixMap = GetGWorldPixMap((**myAppData).fSourceGWorld);
  384.     myDstPixMap = GetGWorldPixMap((**myAppData).fTargetGWorld);
  385.     if ((mySrcPixMap == NULL) || (myDstPixMap == NULL))
  386.         goto bail;
  387.  
  388.     // make the effect sources
  389.     MakeImageDescriptionForPixMap(mySrcPixMap, &(**myAppData).fSourceGWDesc);
  390.     myErr = CDSequenceNewDataSource(
  391.                             myPointer->fSequenceID,
  392.                             &mySrc1,
  393.                             kSourceOneName,
  394.                             1,
  395.                             (Handle)(**myAppData).fSourceGWDesc,
  396.                             NULL,
  397.                             0);
  398.     if (myErr != noErr)
  399.         goto bail;
  400.  
  401.     CDSequenceSetSourceData(mySrc1, GetPixBaseAddr(mySrcPixMap), (**(**myAppData).fSourceGWDesc).dataSize);
  402.  
  403.     MakeImageDescriptionForPixMap(myDstPixMap, &(**myAppData).fTargetGWDesc);
  404.     myErr = CDSequenceNewDataSource(
  405.                             myPointer->fSequenceID,
  406.                             &mySrc2,
  407.                             kSourceTwoName,
  408.                             1,
  409.                             (Handle)(**myAppData).fTargetGWDesc,
  410.                             NULL,
  411.                             0);
  412.     if (myErr != noErr)
  413.         goto bail;
  414.  
  415.     CDSequenceSetSourceData(mySrc2, GetPixBaseAddr(myDstPixMap), (**(**myAppData).fTargetGWDesc).dataSize);
  416.  
  417.     // dispose of any existing time base
  418.     if (myPointer->fTimeBase != NULL)
  419.         DisposeTimeBase(myPointer->fTimeBase);
  420.         
  421.     // create a new time base and associate it with the decompression sequence
  422.     // (this time base MUST be disposed of before the application exits or bad
  423.     // things may happen; we dispose of it when we call VREffects_DumpEntryMem
  424.     // below)
  425.     myPointer->fTimeBase = NewTimeBase();
  426.     myErr = GetMoviesError();
  427.     if (myErr != noErr)
  428.         goto bail;
  429.         
  430.     SetTimeBaseRate(myPointer->fTimeBase, 0);
  431.     myErr = CDSequenceSetTimeBase(myPointer->fSequenceID, myPointer->fTimeBase);
  432.     if (myErr != noErr)
  433.         goto bail;
  434.  
  435.     HLock((Handle)myPointer->fEffectDesc);
  436.     
  437.     // now run the effect thru from start to finish
  438.     for (myFrame = 1; myFrame <= myPointer->fNumberOfSteps; myFrame++) {
  439.  
  440.         // set the timebase time to the step of the sequence to be rendered
  441.         SetTimeBaseValue(myPointer->fTimeBase, myFrame, myPointer->fNumberOfSteps);
  442.         
  443.         myFrameTime.value.hi                = 0;
  444.         myFrameTime.value.lo                = myFrame;
  445.         myFrameTime.scale                    = myPointer->fNumberOfSteps;
  446.         myFrameTime.base                    = 0;
  447.         myFrameTime.duration                = myPointer->fNumberOfSteps;
  448.         myFrameTime.rate                    = 0;
  449.         myFrameTime.recordSize                = sizeof(myFrameTime);
  450.         myFrameTime.frameNumber                = 1;
  451.         myFrameTime.flags                    = icmFrameTimeHasVirtualStartTimeAndDuration;
  452.         myFrameTime.virtualStartTime.hi        = 0;
  453.         myFrameTime.virtualStartTime.lo        = 0;
  454.         myFrameTime.virtualDuration            = myPointer->fNumberOfSteps;
  455.         
  456.         
  457.         myErr = DecompressSequenceFrameWhen(myPointer->fSequenceID,
  458.                                             StripAddress(*((Handle)myPointer->fEffectDesc)),
  459.                                             GetHandleSize((Handle)myPointer->fEffectDesc),
  460.                                             0,
  461.                                             0,
  462.                                             NULL,
  463.                                             &myFrameTime);
  464.         if (myErr != noErr)
  465.             goto bail;
  466.             
  467.         // on very long transitions (or very slow machines), scene-wide sound-only movies need to be serviced
  468.         // (but not every frame, eh?)
  469.         if (myFrame % kDoIdleStep == 0)
  470.             VRMoov_DoIdle(theWindowObject);
  471.     }
  472.  
  473.     // see whether the enlisted effect has expired
  474.     VRScript_CheckForExpiredCommand(theWindowObject, (VRScriptGenericPtr)myPointer);
  475.     
  476.     // if we got this far, all is okay
  477.     myErr = noErr;
  478.     
  479. bail:
  480.     if ((myPointer != NULL) && (myPointer->fEffectDesc != NULL))
  481.         HUnlock((Handle)myPointer->fEffectDesc);
  482.     
  483.     // dispose of the effects GWorlds, if necessary
  484.     VREffects_DumpWindowData(theWindowObject);
  485.     
  486.     // dispose of the effect sequence and time base
  487.     VREffects_DumpEntryMem(myPointer);
  488.     
  489.     // reset the movie and controller GWorlds to the on-screen window
  490.     MCSetControllerPort((**theWindowObject).fController, (CGrafPtr)GetPortFromWindowReference((**theWindowObject).fWindow));
  491.     SetMovieGWorld((**theWindowObject).fMovie, (CGrafPtr)GetPortFromWindowReference((**theWindowObject).fWindow), NULL);
  492.     MCMovieChanged((**theWindowObject).fController, (**theWindowObject).fMovie);
  493.     
  494.     // mark this transition as having been run
  495.     (**myAppData).fActiveTransition = NULL;
  496.     
  497.     if (mySrcPixMap != NULL)
  498.         UnlockPixels(mySrcPixMap);
  499.     if (myDstPixMap != NULL)
  500.         UnlockPixels(myDstPixMap);
  501.  
  502.     return(myErr);
  503. }
  504.  
  505.  
  506. //////////
  507. //
  508. // VREffects_DumpEntryMem
  509. // Release any memory associated with the specified list entry.
  510. //
  511. //////////
  512.  
  513. void VREffects_DumpEntryMem (VRScriptTransitionPtr theEntry)
  514. {    
  515.     if (theEntry != NULL) {
  516.         // if an effect sequence is currently set up, end it
  517.         if (theEntry->fSequenceID != 0L) {
  518.             CDSequenceEnd(theEntry->fSequenceID);
  519.             theEntry->fSequenceID = 0L;
  520.         }
  521.             
  522.         // if a time base currently exists, dispose of it
  523.         if (theEntry->fTimeBase != NULL) {
  524.             DisposeTimeBase(theEntry->fTimeBase);
  525.             theEntry->fTimeBase = NULL;
  526.         }
  527.         
  528.         // dispose of the effect description
  529.         if (theEntry->fEffectDesc != NULL) {
  530.             QTDisposeAtomContainer(theEntry->fEffectDesc);
  531.             theEntry->fEffectDesc = NULL;
  532.         }
  533.         
  534.         // dispose of the sample description
  535.         if (theEntry->fSampleDesc != NULL) {
  536.             DisposeHandle((Handle)theEntry->fSampleDesc);
  537.             theEntry->fSampleDesc = NULL;
  538.         }
  539.     }
  540. }